﻿/*
 * Сделано в SharpDevelop.
 * Пользователь: Badanin M.V.
 * Дата: 22.05.2013
 * Время: 8:48
 * Проект: РПН Регион 2013
 * Распространяется под лицензией GPL v3
 */
using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Security.Cryptography;
using Region.Utils;
using System.Xml.Linq;

namespace Updater
{
  /// <summary>
  /// Class with program entry point.
  /// </summary>
  internal sealed class Program
  {
    class FileData
    {
      public String FilePath;
      public Byte[] MD5;
      public Byte[] Data;
    }
    static void Log(String Message)
    {
      using(FileStream file = File.OpenWrite("./Updater.log"))      
      {
        Byte[] Buff = Encoding.UTF8.GetBytes("["+DateTime.Now.ToUniversalTime()+"."+DateTime.Now.Millisecond+"] "+Message+"\r\n");
        file.Seek(0,SeekOrigin.End);
        file.Write(Buff,0,Buff.Length);
      }      
    }
    private static String ByteToHex(Byte[] Data)
    {
      Char[] TableConv = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
      StringBuilder SB = new StringBuilder();
      for(int i=0;i<Data.Length;i++)
      {
        SB.Append(TableConv[(Data[i] & 0xF0)>>4]);
        SB.Append(TableConv[Data[i] & 0x0F]);
      }
      return SB.ToString();
    }
    private static void LoadFiles(DirectoryInfo Dir,List<FileData> FD)
    {
      foreach(FileInfo File in Dir.EnumerateFiles())
        if (File.Extension.ToUpper()!=".PDB"&&File.Extension.ToUpper()!=".ZIP")
      {        
        var F = new FileData();
        F.FilePath = File.FullName;
        F.Data = System.IO.File.ReadAllBytes(File.FullName);
        F.MD5 = MD5.Create().ComputeHash(F.Data);
        FD.Add(F);
      }
      foreach(DirectoryInfo SubDir in Dir.EnumerateDirectories())
        LoadFiles(SubDir,FD);
    }
    private static void Info(Zip z, List<FileData> FD, String Version)
    {
      XElement Files = new XElement("Files");
      foreach(FileData F in FD)
        Files.Add(new XElement("Version",
                               new XAttribute("FileName",F.FilePath),
                               new XAttribute("MD5",ByteToHex(F.MD5))
                              ));
      XDocument srcTree = new XDocument(
          new XComment("Установочный пакет программы Регион"),
          new XElement("Updater",
            Files,
            new XElement("Version",Version)
           )
          );
      z.writeAll("Info.xml",Encoding.UTF8.GetBytes(srcTree.ToString()));
      z.writeAll("Version",Encoding.UTF8.GetBytes(Version));
    }
    private static void UnpackProcess(String Path,String Packet)
    {
      Log("Начать обновление из пакета ["+Packet+"] в папку"+Path);
      Zip PZ = new Zip();
      PZ.readZIP(Packet);
      String[] Files = PZ.filesList();
      DirectoryInfo Dir = new DirectoryInfo(Path);
      foreach(String FileName in Files)
      {        
        try
        {
          String FullFileName = Dir.FullName+"/"+FileName;
          var FFI = new FileInfo(FullFileName);
          // Создать папку если она несуществует
          if (!FFI.Directory.Exists) Directory.CreateDirectory(FFI.Directory.FullName);
          // Удалить файл если он есть
          if (FFI.Exists) FFI.Delete();
          //Сохранить файл из архива
          File.WriteAllBytes(FullFileName,PZ.readAll(FileName));
          Log("Файл обновлён ("+FileName+")");
        }
        catch(Exception E)
        {
          Log("Ошибка при распоковке файла("+FileName+")"+E.Message);
        }
      }
    }
    private static void PackProcess(String Path,String Version)
    {
      List<FileData> FD = new List<Program.FileData>();
      DirectoryInfo CurrentDir = new DirectoryInfo(Path);
      LoadFiles(CurrentDir, FD);
      Zip z = Zip.EmptyZip();
      foreach(FileData  F in FD)
      {
        F.FilePath = F.FilePath.StartsWith(CurrentDir.FullName)?F.FilePath.Remove(0,CurrentDir.FullName.Length+1) :F.FilePath;
        z.writeAll(F.FilePath,F.Data);        
      }
      Info(z, FD, Version);
      z.writeZIP("Region"+Version+".zip");
      
      
      //z.writeAll();
    }
    private static void WaitProcess(String WaitName)
    {
      Log("Ожидание ("+WaitName+")");
      while(Process.GetProcessesByName(WaitName).Length>0) Thread.Sleep(100);   
    }
    private static void RunProcess(String RunName)
    {
       Log("Запуск ("+RunName+")");
      Process.Start(RunName);
    }
    /// <summary>
    /// Program entry point.
    /// </summary>
    [STAThread]
    private static void Main(string[] args)
    {
      bool Wait = false;      
      String WaitName="Region2";
      bool Run = false;      
      String RunName="Region2";

      bool Pack = false;
      String PackVersion = "1.0.0";
      String PackPath = ".";
      bool Unpack = false;
      String UnpackPacket = "PacketSize";
      String UnpackPath = ".";
      Int32 PN=0;
      while(PN<args.Length)      
      {
        if ((args[PN]=="--pack" || args[PN]=="-p") && PN+3<=args.Length)
        {
          Pack=true;
          PackVersion = args[PN+1];
          PackPath = args[PN+2];
          PN+=3;
        }
        else if ((args[PN]=="--unpack" || args[PN]=="-u") && PN+3<=args.Length)
        {
          Unpack=true;
          
          UnpackPacket = args[PN+1]; 
          UnpackPath = args[PN+2];
          PN+=3;
        }
        else if ((args[PN]=="--wait" || args[PN]=="-w") && PN+2<=args.Length)
        {
          Wait=true;
          WaitName = args[PN+1];          
          PN+=2;
        }
        else if ((args[PN]=="--run" || args[PN]=="-r") && PN+2<=args.Length)
        {
          Run=true;
          RunName = args[PN+1];          
          PN+=2;
        }
        else Console.WriteLine(@"
updater [--pack Версия Путь][--wait Имя_программы]
  -w or --wait Ожидание завершения программы для правильного обновления
  -r or --run Запуск программы по завершению обновления
");
      }
      if (Wait) WaitProcess(WaitName);
      if (Pack) PackProcess(PackPath,PackVersion);
      if (Unpack) UnpackProcess(UnpackPath,UnpackPacket);
      if (Run) RunProcess(RunName);
      /*Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new MainForm());*/
    }
    
  }
}
